<?php

declare(strict_types=1);

namespace JsonDb\JsonDb;

/**
 * 查询构建器 - 负责构建和执行数据查询
 * 
 * 实现WHERE条件过滤、排序和分页功能
 * 使用策略模式处理不同查询操作符
 */
class QueryBuilder
{

	/** @var array 查询条件集合 */
	private array $conditions = [];

	/** @var string|null 排序字段 */
	private ?string $orderField = null;

	/** @var string 排序方向 ASC/DESC */
	private string $orderDirection = 'ASC';

	/** @var int|null 分页偏移量 */
	private ?int $limitOffset = null;

	/** @var int|null 分页长度 */
	private ?int $limitLength = null;

	/**
	 * 添加WHERE条件
	 * 
	 * @param string $field 字段名
	 * @param string $operator 操作符 (=, >, <, LIKE 等)
	 * @param mixed $value 比较值
	 * @return self 返回当前实例以实现链式调用
	 */
	public function where(string $field, string $operator, $value): self
	{
		$this->conditions[] = [
			'type' => 'where',
			'field' => $field,
			'operator' => strtoupper($operator),
			'value' => $value
		];
		return $this;
	}

	/**
	 * 添加排序规则
	 * 
	 * @param string $field 排序字段
	 * @param string $direction 排序方向 (ASC/DESC)
	 * @return self
	 */
	public function orderBy(string $field, string $direction = 'ASC'): self
	{
		$this->orderField = $field;
		$this->orderDirection = strtoupper($direction) === 'DESC' ? 'DESC' : 'ASC';
		return $this;
	}

	/**
	 * 设置分页限制
	 * 
	 * @param int $offset 起始偏移量
	 * @param int|null $length 获取数量
	 * @return self
	 */
	public function limit(int $offset, ?int $length = null): self
	{
		// 处理非法偏移量
		if ($offset < 0) {
			throw new \InvalidArgumentException("偏移量不能小于0");
		}

		// 当length为null时，取到数组末尾
		$this->limitOffset = $offset;
		$this->limitLength = $length ?? PHP_INT_MAX;

		return $this;
	}

	/**
	 * 应用所有查询条件到数据集
	 * 
	 * @param array $data 原始数据集
	 * @return array 过滤排序后的数据集
	 */
	public function apply(array $data): array
	{
		$filtered = $data;

		// 逐步应用所有查询条件
		foreach ($this->conditions as $condition) {
			$filtered = array_filter($filtered, function ($item) use ($condition) {
				return $this->compare(
					$item[$condition['field']] ?? null,
					$condition['operator'],
					$condition['value']
				);
			});
		}

		// 执行排序
		if ($this->orderField) {
			usort($filtered, function ($a, $b) {
				$valA = $a[$this->orderField] ?? null;
				$valB = $b[$this->orderField] ?? null;
				return $this->orderDirection === 'ASC' ? $valA <=> $valB : $valB <=> $valA;
			});
		}

		// 应用分页
		if ($this->limitOffset !== null) {
			$filtered = array_slice(
				$filtered,
				$this->limitOffset,
				$this->limitLength,
				true // 保留键名
			);
		}

		return $filtered;
	}

	/**
	 * 值比较核心逻辑
	 * 
	 * @param mixed $actual 实际值
	 * @param string $operator 操作符
	 * @param mixed $expected 期望值
	 * @return bool 是否符合条件
	 * @throws \InvalidArgumentException 当遇到不支持的操作符时抛出
	 */
	private function compare($actual, string $operator, $expected): bool
	{
		return match ($operator) {
			// 等于比较
			'=' => $actual == $expected,
			// 不等于比较
			'!=' => $actual != $expected,
			// 大于比较
			'>' => $actual > $expected,
			// 大于等于
			'>=' => $actual >= $expected,
			// 小于比较
			'<' => $actual < $expected,
			// 小于等于
			'<=' => $actual <= $expected,
			// 包含在数组内
			'IN' => in_array($actual, (array)$expected),
			// 不包含在数组内
			'NOT IN' => !in_array($actual, (array)$expected),
			// LIKE模糊匹配
			'LIKE' => $this->likeCompare($actual, $expected),
				// 抛出未支持的操作符异常
			default => throw new \InvalidArgumentException("不支持的运算符: $operator")
		};
	}

	/**
	 * 实现LIKE模糊匹配
	 * 
	 * @param string|null $actual 实际字符串
	 * @param string $pattern 匹配模式 (% 匹配多个字符，_ 匹配单个字符)
	 * @return bool 是否匹配
	 */
	private function likeCompare(?string $actual, string $pattern): bool
	{
		// 将SQL LIKE语法转换为正则表达式：
		// % => .*
		// _ => . 
		// 同时转义其他正则特殊字符
		$pattern = str_replace(['%', '_'], ['.*', '.'], preg_quote($pattern, '/'));
		// 执行不区分大小写的匹配
		return (bool)preg_match("/^{$pattern}$/i", (string)$actual);
	}
}
